﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Reflection;

namespace ServerProject
{
    public partial class ServerApp : Form
    {
        private int MAX_connect = 20;
        private static Socket server;
        private static List<Computer> Com_List;
        private static byte[] data = new byte[1024];
        private static string[] StateCom = new string[] { "AVAILABLE", "BUSY" };
        private static string[] StateFile = new string[] { "READY", "PROCESSING", "COMPLETED" };

        public ServerApp()
        {
            Com_List = new List<Computer>();       
            InitializeComponent();
            GridView_Com.AutoGenerateColumns = false;
            GridView.AutoGenerateColumns = false;
            GridView_History.AutoGenerateColumns = false;
            Server_Load();
        }
          /*-------------------------------------------------------------------------------------------------/
         /                                        SERVER CONTROL                                            /
        /-------------------------------------------------------------------------------------------------*/
        private string GetLocalIP()
        {
            IPHostEntry host;
            string localIP = "";
            host = Dns.GetHostEntry(Dns.GetHostName());
            foreach (IPAddress ip in host.AddressList)
            {
                if (ip.AddressFamily == AddressFamily.InterNetwork)
                {
                    localIP = ip.ToString();
                    break;
                }
            }
            return localIP;
        }

        private void Server_Load()
        {
            server = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            string ip = GetLocalIP();
            IPEndPoint ipe = new IPEndPoint(IPAddress.Parse(ip), 9050);
            AddTextBoxText("Server: " + Dns.GetHostByAddress(ipe.Address).HostName + " - " + ipe.Address);
            server.Bind(ipe);
            server.Listen(5);
            for (int i = 0; i < MAX_connect; i++)
            {
                ;
                Thread t = new Thread(() => AcceptConn(server));
                t.Start();
            }
        }

        // Chap nhan ket noi
        private void AcceptConn(Socket server)
        {
            while (true)
            {
                Computer client = new Computer();
                client.Com_Soc = server.Accept();
                IPAddress ip = ((IPEndPoint)client.Com_Soc.RemoteEndPoint).Address;
                client.Com_Name = (Dns.GetHostByAddress(ip).HostName);
                IPEndPoint ipe = (IPEndPoint)client.Com_Soc.RemoteEndPoint;
                client.Com_IP = ipe.Address;
                Com_List.Add(client);
                int index = Com_List.IndexOf(client);
                Com_List[index].Com_ID = index+1;
                AddDataCOM(GridView_Com, Com_List);

                Thread reciever = new Thread(() => Listen(Com_List[index]));
                reciever.Start();
            }
        }

        // Lang nghe client
        private void Listen(Computer client)
        {
            string stringData="";
            while (true)
            {
                stringData = Recieve(client);
                if (stringData != "")
                {
                    if (stringData == "QUIT") break;
                    if (!client.Com_Soc.Connected) break;
                    if (stringData.Equals("READY RECIEVE LIST")) SendList(client);
                    if (stringData.Substring(0, 8).Equals("COMPLETE")) File_Complete(client, stringData);
                }
            }
            client.Com_Soc.Close();
            Com_List.Remove(client);
            AddDataCOM(GridView_Com, Com_List);
            Thread.Sleep(100);
        }

        // Nhan tin nhan tu client
        private string Recieve(Computer client)
        {
            try
            {
                int recvdata;
                string str_recv;
                recvdata = client.Com_Soc.Receive(data);
                str_recv = Encoding.ASCII.GetString(data, 0, recvdata);
                return str_recv;
            }
            catch{}
            return null;
        }

        // Gui 1 tin nhan toi client
        private void Send(Computer pc, string str)
        {
            try
            {
                byte[] mess = Encoding.ASCII.GetBytes(str);
                pc.Com_Soc.Send(mess);
            }
            catch{}
        }

        // Gui 1 List<FILE> toi client
        private void SendList(Computer client)
        {
            NetworkStream stream;
            stream = new NetworkStream(client.Com_Soc);
            DATA_SEND datasend = new DATA_SEND();
            datasend.FILE_LIST = client.Files_list;
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, datasend);
            stream.Close();
        }

        // Gui 1 FILE toi client (Send FILE removed)
        private void SendFile(Computer client, FILE f)
        {
            NetworkStream stream;
            stream = new NetworkStream(client.Com_Soc);
            DATA_SEND datasend = new DATA_SEND();
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Serialize(stream, f);
            stream.Close();
        }

        // Nhan 1 FILE tu client (Recieve FILE completed)
        private FILE RecieveFile(Computer client)
        {
            Send(client, "READY RECIEVE FILE");
            NetworkStream stream;
            stream = new NetworkStream(client.Com_Soc);
            BinaryFormatter formatter = new BinaryFormatter();
            formatter.Binder = new AllowAllAssemblyVersionsDeserializationBinder();
            FILE f = (FILE)formatter.Deserialize(stream);
            stream.Close();
            return f;
        }


        private void File_Complete(Computer client, string str)
        {
            int id = int.Parse(str.Substring(8, str.Length - 8).Trim());
            FILE completed_file = client.Files_list[client.Index_of_File(id)];
            int index = client.Files_list.IndexOf(completed_file);
            if (index > -1)
            {
                client.Files_list.RemoveAt(index);
                completed_file.FileState = StateFile[2];
                completed_file.FileDTComplete = DateTime.Now;
                client.History_list.Add(completed_file);
                AddFile(GridView_History, client.History_list);
                Computer com = (Computer)GridView_Com.SelectedRows[0].DataBoundItem;
                if (client.Files_list.Count == 0) client.Com_State = "AVAILABLE";
                AddDataCOM(GridView_Com, Com_List);
                if (com.Com_ID == client.Com_ID)
                {
                    AddFile(GridView, client.Files_list);
                    AddFile(GridView_History, client.History_list);
                    if (Com_List[index].Com_State.Equals("AVAILABLE")) ButtonEnable(btn_Start, true);
                    else ButtonEnable(btn_Start, false);
                }
            }
            
        }

          /*-------------------------------------------------------------------------------------------------/
         /                                     XU LY TREN GRIDVIEW, TEXTBOX                                 /
        /-------------------------------------------------------------------------------------------------*/
        private void AddTextBoxText(string str)
        {
            if (lab_server.InvokeRequired) lab_server.Invoke(new AddTextBoxTextDelegate(this.AddTextBoxText), str);
            else lab_server.Text = str;
        }

        private void AddDataCOM(DataGridView gridview, List<Computer> list)
        {
            if (gridview.InvokeRequired) gridview.Invoke(new AddDataDelegate(this.AddDataCOM), gridview, list);
            else
            {
                gridview.DataSource = null;
                gridview.DataSource = list;
            }
        }

        private void AddFile(DataGridView gridview, List<FILE> list)
        {
            if (gridview.InvokeRequired) gridview.Invoke(new AddFileDelegate(this.AddFile), gridview, list);
            else
            {
                gridview.DataSource = null;
                List<FILE> lf = new List<FILE>();
                foreach (FILE f in list) lf.Add(f);
                gridview.DataSource = lf;
            }
        }

        private void ButtonEnable(Button btn, bool enable)
        {
            if (btn.InvokeRequired) btn.Invoke(new ButtonEnableDelegate(this.ButtonEnable), btn, enable);
            else btn.Enabled = enable;
        }

        private delegate void AddTextBoxTextDelegate(string str);
        private delegate void AddDataDelegate(DataGridView gridview, List<Computer> list);
        private delegate void AddFileDelegate(DataGridView gridview, List<FILE> list);
        private delegate void ButtonEnableDelegate(Button btn, bool enable);

          /*-------------------------------------------------------------------------------------------------/
         /                                        FORM EVENTS                                               /
        /-------------------------------------------------------------------------------------------------*/
        private void OpenFile(Computer com)
        {
            List<FILE> files = new List<FILE>();
            int i = com.Files_list.Count;
            OpenFileDialog OpenFile = new OpenFileDialog();
            OpenFile.Multiselect = true;
            if (OpenFile.ShowDialog() == DialogResult.OK)
            {
                foreach (string file in OpenFile.FileNames)
                {                    
                    FILE f = new FILE(i + 1, file, StateFile[0]);
                    f.FileDTComplete = DateTime.Now;
                    i++;
                    com.Files_list.Add(f);
                }
            }
        }

        private void btn_Select_Click(object sender, EventArgs e)
        {
            if (GridView_Com.SelectedRows.Count == 0) MessageBox.Show("Please select computer!");
            else
            {
                Computer selected_com = (Computer)GridView_Com.SelectedRows[0].DataBoundItem;
                OpenFile(selected_com);
                AddFile(GridView, selected_com.Files_list);
            }
        }

        private void GridView_Com_RowEnter(object sender, DataGridViewCellEventArgs e)
        {
            int index = e.RowIndex;
            if (index >= 0)
            {
                AddFile(GridView, Com_List[index].Files_list);
                AddFile(GridView_History, Com_List[index].History_list);
                if (Com_List[index].Com_State.Equals("AVAILABLE")) ButtonEnable(btn_Start, true);
                else ButtonEnable(btn_Start, false);
            }
        }

        private void btn_Start_Click(object sender, EventArgs e)
        {
            if (GridView_Com.SelectedRows.Count == 0) MessageBox.Show("Please select computer!");
            else
            {
                Computer selected_com = (Computer)GridView_Com.SelectedRows[0].DataBoundItem;
                if (selected_com.Files_list.Count > 0)
                {
                    Send(Com_List[selected_com.Com_ID - 1], "SEND LIST FILE");
                    selected_com.Com_State = "BUSY";
                    AddDataCOM(GridView_Com, Com_List);
                    ButtonEnable(btn_Start, false);
                }
                else MessageBox.Show("Please insert file in files list!");
            }
        }

        private void GridView_CellContentClick(object sender, DataGridViewCellEventArgs e)
        {
            if (GridView.Columns[e.ColumnIndex].HeaderText == "Remove")
                if (MessageBox.Show("Do you want to remove this file?", "Request", MessageBoxButtons.YesNo, MessageBoxIcon.Question) == DialogResult.Yes)
                {
                    Computer selected_com = (Computer)GridView_Com.SelectedRows[0].DataBoundItem;
                    FILE file_remove = (FILE)GridView.SelectedRows[0].DataBoundItem;
                    selected_com.Files_list.Remove(file_remove);
                    AddFile(GridView, selected_com.Files_list);
                    Send(selected_com, "REMOVE " + file_remove.FileID);
                }
        }

        private void ServerApp_FormClosing(object sender, FormClosingEventArgs e)
        {
            server.Close();
        }

        private void button4_Click(object sender, EventArgs e)
        {
            server.Close();
            this.Dispose();
        }

        private void GridView_DragDrop(object sender, DragEventArgs e)
        {
            if (GridView_Com.SelectedRows.Count == 0) MessageBox.Show("Please select computer!");
            else
            {
                Computer selected_com = (Computer)GridView_Com.SelectedRows[0].DataBoundItem;
                if (e.Data.GetDataPresent(DataFormats.FileDrop))
                {
                    string[] Fs = (string[])e.Data.GetData(DataFormats.FileDrop);
                    foreach (string file in Fs)
                    {
                        int i = selected_com.Files_list.Count;
                        FILE f = new FILE(i, file, StateFile[0]);
                        i++;
                        selected_com.Files_list.Add(f);
                    }
                }                          
                AddFile(GridView, selected_com.Files_list);
            }
        }

        private void GridView_DragEnter(object sender, DragEventArgs e)
        {
            if (e.Data.GetDataPresent(DataFormats.FileDrop))
            {
                e.Effect = DragDropEffects.Copy;
            }
            else
            {
                e.Effect = DragDropEffects.None;
            }
        }




    }

    sealed class AllowAllAssemblyVersionsDeserializationBinder : SerializationBinder
    {
        public override Type BindToType(string assemblyName, string typeName)
        {
            if (typeName.Contains("System.Collections.Generic.List"))
                return typeof(List<FILE>);
            Type typeToDeserialize = null;
            String currentAssembly = Assembly.GetExecutingAssembly().FullName;
            // In this case we are always using the current assembly
            assemblyName = currentAssembly;
            // Get the type using the typeName and assemblyName
            typeToDeserialize = Type.GetType(String.Format("{0}, {1}", typeName, assemblyName));
            return typeToDeserialize;
        }
    }
}
